<?php
// +----------------------------
// | Created by PhpStorm
// +----------------------------
// | Author: YnnSon
// +----------------------------
namespace app\api\service;

use app\api\model\Order as OrderModel;
use app\lib\enum\OrderStatusEnum;
use app\lib\exception\OrderException;
use app\lib\exception\TokenException;
use think\Config;
use think\Exception;
use think\Log;
use think\Request;
use WeChat\Pay as PaySDK;

/**
 * 支付
 * Class Pay
 * @package app\api\service
 */
class Pay
{
    // 订单ID
    private $order_id;
    // 订单编号
    private $order_sn;
    // 订单总价
    private $total_fee;
    // 微信支付参数
    private $payConfig;

    public function __construct($order)
    {
        $this->order_id = $order['order_id'];
        $this->order_sn = $order['order_sn'];
        $this->total_fee = $order['total_amount'];
        $this->payConfig = Config::get('wechat');
    }

    /**
     * @Desc 支付主方法
     * @return array
     */
    public function pay()
    {
        // 1.检查订单有效性
        $this->checkOrderValidity();
        // 2.设置预订单参数
        $options = $this->setPreOrderParams($this->total_fee);
        // 3.统一下单, 并将结果返回客户端
        return $this->unifiedOrder($options);
    }

    /**
     * @Desc (1)order_id是否存在
     * @Desc (2)(存在)order_id与当前用户是否匹配
     * @Desc (3)(匹配)订单是否被支付过
     * @return bool
     */
    private function checkOrderValidity()
    {
        $order = OrderModel::getOrderDetail($this->order_id);
        if (!$order) {
            throw new OrderException();
        }
        if ($this->total_fee != $order['total_amount']) {
            throw new OrderException([
                'code'      => 400,
                'message'   => '订单金额不匹配',
                'errorCode' => 40002,
            ]);
        }
        if (!Token::checkUid($order['user_weixin_id'])) {
            throw new TokenException([
                'code'      => 403,
                'message'   => '订单与用户不匹配',
                'errorCode' => 10003,
            ]);
        }
        if ($order['order_status'] != OrderStatusEnum::UNPAID) {
            throw new OrderException([
                'code'      => 400,
                'message'   => '订单已支付',
                'errorCode' => 40003,
            ]);
        }
        return true;
    }

    /**
     * @Desc 设置预订单参数
     *
     * @param float  $total_fee   订单总价
     *
     * @return array
     */
    private function setPreOrderParams($total_fee)
    {
        // 将订单单位元转换为微信需要的单位为分
        $total_fee *= 100;
        $openid = Token::getCacheValueByToken('openid');
        if (!$openid) {
            throw new TokenException();
        }
        return [
            'body'             => $this->payConfig['order_body'],
            'detail'           => $this->payConfig['order_detail'],
            'out_trade_no'     => $this->order_sn, // 订单号
            'total_fee'        => (int)$total_fee, // 订单总价, 单位分
            // 'total_fee'        => 1, // 订单总价, 测试数据
            'spbill_create_ip' => Request::instance()->server('remote_addr'), // 调用微信支付API的机器IP
            'notify_url'       => $this->payConfig['notify_url'], // 支付结果通知网址，如果不设置则会使用配置里的默认地址
            'trade_type'       => 'JSAPI',
            'openid'           => $openid,
        ];
    }

    /**
     * @Desc 调用的wechat-developer
     * @Desc 统一下单, 并获取微信返回结果
     *
     * @param array $options 预订单参数
     *
     * @return array
     */
    private function unifiedOrder($options)
    {
        try {
            // 创建接口实例
            $pay = new PaySDK($this->payConfig);
            // 尝试创建订单
            $order = $pay->createOrder($options);
            // 失败
            if ($order['return_code'] != 'SUCCESS') {
                // 将错误信息记录日志
                Log::error($order);
                Log::error('获取预支付订单失败');
                throw new Exception($order['return_msg']);
            }
            if ($order['result_code'] != 'SUCCESS') {
                // 将错误信息记录日志
                Log::error($order);
                Log::error('获取预支付订单失败');
                if ('201 商户订单号重复' == $order['err_code_des']) {
                    throw new OrderException([
                        'code'      => 400,
                        'message'   => '系统更新，请重新下单',
                        'errorCode' => 40001
                    ]);
                }
                throw new Exception($order['err_code_des']);
            }
            $prepayId = $order['prepay_id'];
            // 将预支付订单ID存入数据库
            $this->recordPreOrder($prepayId);
            // 创建微信端发起支付参数及签名
            // 微信端发起支付参数及签名JSON化
            return $pay->createParamsForJsApi($prepayId);
        } catch (Exception $e) {
            // 出错啦，处理下吧
            throw $e;
        }
    }

    /**
     * @Desc 将prepay_id存入数据库order表
     *
     * @param string $prepayId
     */
    private function recordPreOrder($prepayId)
    {
        OrderModel::update(['prepay_id' => $prepayId], ['order_id' => $this->order_id]);
    }
}